package cn.bigcontainer.alt.meta.service.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import cn.bigcontainer.alt.common.entity.BigMap;
import cn.bigcontainer.alt.common.util.WoKit;
import cn.bigcontainer.alt.common.util.WoPinyinKit;
import cn.bigcontainer.alt.meta.dao.MDao;
import cn.bigcontainer.alt.meta.dto.MAppDto;
import cn.bigcontainer.alt.meta.dto.MCrud;
import cn.bigcontainer.alt.meta.dto.MDictionaryDto;
import cn.bigcontainer.alt.meta.dto.MObjectDto;
import cn.bigcontainer.alt.meta.dto.MViewDto;
import cn.bigcontainer.alt.meta.exception.MException;
import cn.bigcontainer.alt.meta.mapper.MAppMapper;
import cn.bigcontainer.alt.meta.mapper.MDictionaryMapper;
import cn.bigcontainer.alt.meta.mapper.MObjectMapper;
import cn.bigcontainer.alt.meta.mapper.MViewMapper;
import cn.bigcontainer.alt.meta.po.MApp;
import cn.bigcontainer.alt.meta.po.MButton;
import cn.bigcontainer.alt.meta.po.MDictionary;
import cn.bigcontainer.alt.meta.po.MObject;
import cn.bigcontainer.alt.meta.po.MProperty;
import cn.bigcontainer.alt.meta.po.MRelation;
import cn.bigcontainer.alt.meta.po.MView;
import cn.bigcontainer.alt.meta.po.MViewProperty;
import cn.bigcontainer.alt.meta.service.MetaBizService;
import cn.bigcontainer.alt.meta.service.MetaService;
import cn.bigcontainer.alt.meta.util.MUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 业务视图元数据管理Service
 * 
 * @author jiaod
 *
 */
@Service
@Transactional(rollbackFor = { Exception.class })
@Primary
@Slf4j
public class MetaBizServiceImpl implements MetaBizService {

	@Resource
	private MAppMapper appMapper;

	@Resource
	private MViewMapper viewMapper;

	@Resource
	private MObjectMapper objectMapper;
	
	@Resource
	private MDao mDao;
	
	@Override
	public List<Map<String, Object>> searchObject(String searchContent, String keyValue) {
		String sql = "select id as value, CONCAT_WS('|', id,name,no) as name, 'm_object' as type from m_object ";
		if (keyValue == null) {
			sql += String.format("where id like '%%%s%%' or name like '%%%s%%' or no like '%%%s%%' limit 20", searchContent, searchContent, searchContent);
		} else {
			String[] valStrings = keyValue.split("[:,]");
			String idString = "";
			for (int i = 0; i < valStrings.length; i = i + 2) {
				if (!"".equals(idString)) {
					idString += ",";
				}
				idString += String.format("'%s'", valStrings[i + 1]);
			}
			sql += String.format("where id in (%s)", idString);
		}
		return mDao.queryList(sql);
	}
	
	@Override
	public List<Map<String, Object>> searchProperty(String object, String relation) {
		MObject mObject = objectMapper.selectById(object);
		List<Map<String, Object>> dataList = new ArrayList<Map<String,Object>>();
		if (WoKit.isEmpty(relation)) {
			setPropertyData(mObject, dataList);
		} else {
			MRelation mRelation = MObjectDto.getRelation(mObject, relation);
			List<MObject> objects = objectMapper.selectBatchIds(Arrays.asList(mRelation.getRelatedObjectId().split(",")));
			objects.forEach(o -> {
				setPropertyData(o, dataList);
			});
		}
		return dataList;
	}

	private void setPropertyData(MObject mObject, List<Map<String, Object>> dataList) {
		mObject.getProperties().forEach(p -> {
			Map<String, Object> dataMap = new HashMap<>();
			dataMap.put("name", mObject.getName() + "-" + p.getDescription());
			dataMap.put("value", p.getId());
			dataList.add(dataMap);
		});
	}

	@Override
	public List<Map<String, Object>> searchRelation(String object) {
		MObject mObject = objectMapper.selectById(object);
		List<Map<String, Object>> dataList = new ArrayList<Map<String,Object>>();
		mObject.getRelations().forEach(p -> {
			Map<String, Object> dataMap = new HashMap<>();
			dataMap.put("name", p.getName() + "|" + p.getType());
			dataMap.put("value", p.getId());
			dataList.add(dataMap);
		});
		return dataList;
	}
	
	private MViewProperty getViewProperty(String app, String viewproperty) {
		MApp mApp = appMapper.selectById(app);
		MAppDto appDto = new MAppDto(mApp);
		MViewProperty targetVp = appDto.getViewProperty(viewproperty);
		return targetVp;
	}
	
	private MViewDto getView(String app, String view) {
		MApp mApp = appMapper.selectById(app);
		MAppDto appDto = new MAppDto(mApp);
		MViewDto targetView = appDto.getView(view);
		return targetView;
	}

	private void saveViewByProperty(MViewProperty targetVp) {
		// 保存到数据库
		MView mView = viewMapper.selectById(targetVp.getView().getId());
		mView.setProperties(targetVp.getView().getProperties());
		viewMapper.updateById(mView);
	}
	
	private void saveByView(MViewDto mViewDto) {
		// 保存到数据库
		MView mView = viewMapper.selectById(mViewDto.getId());
		mView.setProperties(mViewDto.getProperties());
		viewMapper.updateById(mView);
	}

	@Override
	public void moveViewProperty(String app, String viewproperty, Integer offset) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		MViewDto view = targetVp.getView();
		List<MViewProperty> properties = view.getProperties();
		// 所有元素设置初始索引，规则：位置 * 10
		for (int i = 0; i < properties.size(); i++) {
			MViewProperty vp = properties.get(i);
			vp.setIndex((i + 1) * 10);
		}
		// targetVp在properties中的位置
		int targetVisible = -1;
		// targetVp之前的元素位置
		int lastVisible = -1;
		for (int i = 0; i < properties.size(); i++) {
			MViewProperty vp = properties.get(i);
			if (MViewProperty.TYPE_HIDDEN.equals(vp.getType())) {
				continue;
			}
			if (offset < 0) {// 前移，将targetVp属性的索引设置到之前一个非hidden属性之前
				if (vp == targetVp && lastVisible > -1) {
					vp.setIndex((lastVisible + 1) * 10 - 5);
					break;
				}
				lastVisible = i;
			} else {// 后移，将后一个非hidden属性的索引设置到targetVp之前
				if (vp == targetVp) {
					targetVisible = i;
					continue;
				}
				if (targetVisible > -1 && i > targetVisible) {
					vp.setIndex((targetVisible + 1) * 10 - 5);
					break;
				}
			}
		}
		Collections.sort(properties);
		// 保存到数据库
		saveViewByProperty(targetVp);
	}

	@Override
	public void hideViewProperty(String app, String viewproperty) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		targetVp.getData().bigPut("oldType", targetVp.getType());
		targetVp.setType(MViewProperty.TYPE_HIDDEN);
		if (!targetVp.getView().hasVisibleProperties()) {
			throw new MException(MException.BIZ_VISIBLE, targetVp.getView().getName());
		}
		saveViewByProperty(targetVp);
	}

	@Override
	public void showViewProperty(String app, String viewproperty, String hidden) {
		// MViewProperty targetVisibleViewProperty = getViewProperty(app, viewproperty);
		MViewProperty targetHiddenViewProperty = getViewProperty(app, hidden);
		MViewDto view = targetHiddenViewProperty.getView();
		view.reIndexProperties();
		List<MViewProperty> properties = view.getProperties();
		// 设置类型为非hidden类型
		String oldType = targetHiddenViewProperty.getData().getString("oldType", MViewProperty.TYPE_TEXT);
		targetHiddenViewProperty.setType(oldType);
		// 设置hidden视图属性的索引在viewproperty视图属性之后
		targetHiddenViewProperty.setIndex(view.getProperty(viewproperty).getIndex() + 5);
		Collections.sort(properties);
		saveViewByProperty(targetHiddenViewProperty);
	}

	@Override
	public void deleteViewProperty(String app, String viewproperty, Boolean cascade) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		MViewDto targetView = targetVp.getView();
		// 如果是级联，还需要删除关联属性、属性关联的视图属性以及对应表字段
		if (cascade && (targetVp.getRelationId() != null || targetVp.getPropertyId() != null)) {
			MObjectDto object = targetVp.getView().getApp().getObject();
			// 如果关系为空，属性不为空
			if (targetVp.getRelationId() == null) {
				// 1.遍历所有视图对象，查找与targetVp对应共同属性的视图属性并删除
				List<MView> viewList = viewMapper.selectList(null);
				viewList.forEach(v -> {
					List<MViewProperty> properties = v.getProperties();
					Boolean removeBoolean = false;
					// 采用iterator删除集合中多个元素，也可以采用for循环逆序遍历
					for (Iterator<MViewProperty> iterator = properties.iterator(); iterator.hasNext();) {
						MViewProperty mViewProperty = iterator.next();
						if (targetVp.getPropertyId().equals(mViewProperty.getPropertyId())) {
							iterator.remove();
							removeBoolean = true;
						}
					}
					if (removeBoolean) {
						if (!MViewDto.hasVisibleProperties(v)) {
							throw new MException(MException.BIZ_VISIBLE, v.getName());
						}
						viewMapper.updateById(v);
					}
				});
				// 2.删除属性和表字段
				MObject mObject = objectMapper.selectById(object.getId());
				for (Iterator<MProperty> iterator = mObject.getProperties().iterator(); iterator.hasNext();) {
					MProperty mProperty = iterator.next();
					if (mProperty.getId().equals(targetVp.getPropertyId())) {
						iterator.remove();
						// 更新元数据
						objectMapper.updateById(mObject);
						// 删除表字段
						mDao.update(mProperty.getDropSql(mObject));
						break;
					}
				}
				return;
			}
			// TODO 关系不为空
			return;
		}
		// 删除视图属性
		if (targetView.getType().equals(MViewDto.TYPE_LIST)) {
			if (targetView.getNameProperty() == targetVp) {
				throw new MException(MException.META_DEL_VP);
			}
		}
		targetView.getProperties().remove(targetVp);
		if (!targetVp.getView().hasVisibleProperties()) {
			throw new MException(MException.BIZ_VISIBLE, targetVp.getView().getName());
		}
		saveViewByProperty(targetVp);
	}

	@Override
	public void resizeViewProperty(String app, String viewproperty, Integer width, Integer height) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		if (width == -1 && targetVp.getView().isList()) {
			targetVp.getView().getProperties().forEach(p -> {
				if (p.getWidth() == -1) {
					p.setWidth(100);
				}
			});
		}
		targetVp.setWidth(width);
		targetVp.setHeight(height);
		saveViewByProperty(targetVp);
	}

	@Override
	public void orderViewProperty(String app, String viewproperty, String ordertype) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		setOrder(1, ordertype, targetVp);
		saveViewByProperty(targetVp);
	}

	private void setOrder(Integer order, String ordertype, MViewProperty targetVp) {
		if (order == -1) {
			return;
		}
		List<MViewProperty> orderProperties = targetVp.getView().getOrderProperties();
		if (!orderProperties.contains(targetVp)) {
			if (orderProperties.size() > 0) {
				targetVp.setOrderIndex(orderProperties.get(orderProperties.size() - 1).getOrderIndex() + 10);
			} else {
				targetVp.setOrderIndex(0);
			}
		}
		targetVp.setOrderType(ordertype);
	}

	@Override
	public void toggleOrderViewProperty(String app, String viewproperty) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		targetVp.toggleOrderType();
		saveViewByProperty(targetVp);
	}

	@Override
	public void unorderViewProperty(String app, String viewproperty) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		targetVp.setOrderIndex(-1);
		saveViewByProperty(targetVp);
	}

	@Override
	public void unorderView(String app, String view) {
		MViewDto targetView = getView(app, view);
		targetView.getProperties().forEach(p -> {
			p.setOrderIndex(-1);
		});
		saveByView(targetView);
	}

	/**
	 * *加载视图属性，页面：/adminlte-platform/src/main/resources/templates/meta/biz/modal-update-viewproperty.html
	 */
	@Override
	public Map<String, Object> loadViewProperty(String app, String viewproperty) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		Map<String, Object> dataMap = new HashMap<>();
		dataMap.put("id", targetVp.getId());
		dataMap.put("name", targetVp.getName());
		dataMap.put("relation", targetVp.getRelationId());
		dataMap.put("property", targetVp.getPropertyId());
		dataMap.put("type", targetVp.getType());
		dataMap.put("customtype", targetVp.getType());
		dataMap.put("width", targetVp.getWidth());
		dataMap.put("height", targetVp.getHeight());
		dataMap.put("dictionarytype", targetVp.getDictionaryType());
		dataMap.put("expression", targetVp.getExpression());
		if (targetVp.getView().getType().equals(MViewDto.TYPE_LIST)) {
			dataMap.put("orderindex", targetVp.getOrderIndex());
			dataMap.put("ordertype", targetVp.getOrderType());
			dataMap.put("listajax", targetVp.getAjax());
			dataMap.put("listsql", targetVp.getAjaxSql());
		}
		if (targetVp.getView().getType().equals(MViewDto.TYPE_FORM)) {
			dataMap.put("forminitvalue", targetVp.getInitValue());
			dataMap.put("forminitreplaceempty", targetVp.isReplaceInit(null) ? 1 : 0);
			dataMap.put("forminitreplacevalue", targetVp.isReplaceInit("any") ? 1 : 0);
			dataMap.put("formsubmitvalue", targetVp.getSubmitValue());
			dataMap.put("formsubmitreplaceempty", targetVp.isReplaceSubmit(null) ? 1 : 0);
			dataMap.put("formsubmitreplacevalue", targetVp.isReplaceSubmit("any") ? 1 : 0);
			dataMap.put("required", targetVp.getAllowsBlank() == 1 ? 0 : 1);
			dataMap.put("readonly", targetVp.getReadOnly());
			dataMap.put("submit", targetVp.getSubmit());
			// objectInputor
			dataMap.put("searchurl", targetVp.getSearchUrl());
			dataMap.put("dataurl", targetVp.getDataUrl());
		}
		if (targetVp.getView().getType().equals(MViewDto.TYPE_SEARCH)) {
			dataMap.put("comparetype", targetVp.getCompareType());
			dataMap.put("searchexpression", targetVp.getCompareExpression());
			dataMap.put("listfilter", targetVp.getListFilter());
		}
		return dataMap;
	}

	@Override
	public void updateViewProperty(String app, String viewproperty, Map<String, Object> r) {
		BigMap<String, Object> req = new BigMap<String, Object>().bigPutAll(r);
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		// 去重
		for (MViewProperty vp : targetVp.getView().getProperties()) {
			if (vp.getId().equals(targetVp.getId())) {
				continue;
			}
			if (vp.getPropertyId() != null) {
				if (targetVp.getPropertyId() != null && vp.getPropertyId().equals(targetVp.getPropertyId())) {
					throw new MException(MException.BIZ_CREATED, vp.getPropertyId());
				}
			} else {
				if(targetVp.getRelationId() != null && (targetVp.getPropertyId() == null && targetVp.getRelationId().equals(vp.getRelationId()))) {
					throw new MException(MException.BIZ_CREATED, targetVp.getRelationId());
				}
			}
		}
		targetVp.setName(req.getString("name"));
		targetVp.setRelationId(req.getString("relation"));
		targetVp.setPropertyId(req.getString("property"));
		targetVp.setType(req.getString("type"));
		if (!WoKit.isEmpty(req.getString("customtype"))) {
			targetVp.setType(req.getString("customtype"));
		}
		targetVp.setWidth(req.getInt("width"));
		targetVp.setHeight(req.getInt("height"));
		targetVp.setDictionaryType(req.getString("dictionarytype"));
		targetVp.setExpression(req.getString("expression"));
		if (targetVp.getView().isList()) {
			targetVp.setOrderIndex(req.getInt("orderindex"));
			targetVp.setOrderType(req.getString("ordertype"));
			targetVp.setAjax(req.getString("listajax"));
			targetVp.setAjaxSql(req.getString("listsql"));
		}
		if (targetVp.getView().isForm()) {
			targetVp.setInitValue(req.getString("forminitvalue"));
			targetVp.setReplaceInit(req.getInt("forminitreplaceempty", 0) + req.getInt("forminitreplacevalue", 0) * 2);
			targetVp.setSubmitValue(req.getString("formsubmitvalue"));
			targetVp.setReplaceSubmit(req.getInt("formsubmitreplaceempty", 0) + req.getInt("formsubmitreplacevalue", 0) * 2);
			targetVp.setAllowsBlank(req.getInt("required", 0) == 1 ? 0 : 1);
			targetVp.setReadOnly(req.getInt("readonly", 0));
			targetVp.setSubmit(req.getInt("submit", 0));
			targetVp.setSearchUrl(req.getString("searchurl"));
			targetVp.setDataUrl(req.getString("dataurl"));
		}
		if (targetVp.getView().isSearch()) {
			targetVp.setCompareType(req.getString("comparetype"));
			targetVp.setCompareExpression(req.getString("searchexpression"));
			targetVp.setListFilter(req.getString("listfilter"));
		}
		saveViewByProperty(targetVp);
	}
	
	private final static String[] PROP_TYPES = new String[] {"文本", "数字", "整数", "小整数", "日期", "日期时间"};

	@Resource
	private MetaService metaService;
	
	@Override
	public void createViewProperty(String app, String viewproperty, Boolean select, Map<String, Object> req) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		BigMap<String, Object> bigMap = new BigMap<String, Object>().bigPutAll(req);
		if (!select) {
			WoKit.getInt(req.get("type"), 0);
			Integer typeInteger = bigMap.getInt("type", 0);
			String detailString = "类型:" + PROP_TYPES[typeInteger - 1];
			if (typeInteger == 1) {
				detailString += ",长度:" + bigMap.getInt("length");
			} else if (typeInteger == 2) {// 此处的长度指整数位
				detailString += ",长度:" + (bigMap.getInt("precision") - bigMap.getInt("scale")) + ",小数位:" + bigMap.getInt("scale");
			}
			if (!bigMap.isEmpty("default")) {
				detailString += ",默认值:" + bigMap.getString("default");
			}
			if (targetVp.getProperty() != null) {
				detailString += ",索引:" + (targetVp.getProperty().getIndex() + 1);
			} else {
				detailString += ",索引:" + 100;
			}
			MProperty mProperty = metaService.createPropertyMeta(targetVp.getView().getObjectId(), bigMap.getString("name"), detailString);
			createViewProperties(targetVp, bigMap, mProperty, null);
		} else {
			MProperty mProperty = targetVp.getView().getApp().getObject().getPropertyIncludeRelatedObjects(bigMap.getString("property"));
			MRelation mRelation = targetVp.getView().getApp().getObject().getRelation(bigMap.getString("relation"));
			createViewProperties(targetVp, bigMap, mProperty, mRelation);
		}
	}

	private void createViewProperties(MViewProperty targetVp, BigMap<String, Object> bigMap, MProperty mProperty, MRelation mRelation) {
		MViewDto view = targetVp.getView();
		List<MViewDto> views = targetVp.getView().getApp().getViews();
		List<String> viewtype = bigMap.getStringList("viewtype");
		if (viewtype.contains(MViewDto.TYPE_LIST)) {
			if (view.isList()) {
				createViewProperty(targetVp, bigMap, mProperty, mRelation);
			} else {
				views.forEach(v -> {
					if (v.isList()) {
						MViewProperty mViewProperty = v.getViewPropertyByPropertyId(targetVp.getPropertyId());
						if (mViewProperty == null) {
							mViewProperty = v.getProperties().get(v.getProperties().size() - 1);
						}
						createViewProperty(mViewProperty, bigMap, mProperty, mRelation);
					}
				});
			}
		}
		if (viewtype.contains(MViewDto.TYPE_FORM)) {
			String allformString = bigMap.getString("allform");
			if (view.isForm() && !"1".equals(allformString)) {
				createViewProperty(targetVp, bigMap, mProperty, mRelation);
			} else {
				views.forEach(v -> {
					if (v.isForm()) {
						MViewProperty mViewProperty = v.getViewPropertyByPropertyId(targetVp.getPropertyId());
						if (mViewProperty == null) {
							mViewProperty = v.getProperties().get(v.getProperties().size() - 1);
						}
						createViewProperty(mViewProperty, bigMap, mProperty, mRelation);
					}
				});
			}
		}
		if (viewtype.contains(MViewDto.TYPE_SEARCH)) {
			if (view.isSearch()) {
				createViewProperty(targetVp, bigMap, mProperty, mRelation);
			} else {
				views.forEach(v -> {
					if (v.isSearch()) {
						MViewProperty mViewProperty = v.getViewPropertyByPropertyId(targetVp.getPropertyId());
						if (mViewProperty == null) {
							mViewProperty = v.getProperties().get(v.getProperties().size() - 1);
						}
						createViewProperty(mViewProperty, bigMap, mProperty, mRelation);
					}
				});
			}
		}
	}

	/**
	 * @param targetVp 新创建的视图属性应该在targetVp之后
	 * @param bigMap
	 * @param mProperty
	 * @param mRelation
	 */
	private void createViewProperty(MViewProperty targetVp, BigMap<String, Object> bigMap, MProperty mProperty, MRelation mRelation) {
		MViewDto view = targetVp.getView();
		// 去重
		for (MViewProperty vp : view.getProperties()) {
			if (vp.getPropertyId() != null) {
				if (mProperty != null && vp.getPropertyId().equals(mProperty.getId())) {
					throw new MException(MException.BIZ_CREATED, vp.getPropertyId());
				}
			} else {
				if(mRelation != null && mRelation.getId().equals(vp.getRelationId())) {
					throw new MException(MException.BIZ_CREATED, mRelation.getId());
				}
			}
		}
		view.reIndexProperties();
		MViewProperty lp = new MViewProperty();
		lp.setView(view);
		lp.setDictionaryType(bigMap.getString("dictionarytype"));
		lp.setExpression(bigMap.getString("expression"));
		if (mRelation == null) {
			if (mProperty == null) {
				lp.setName(bigMap.getString("viewpropertyname"));
				lp.setId(MUtil.createViewPropertyId(view.getId(), WoPinyinKit.getPinyinLower(lp.getName())));
			} else {
				lp.setId(MUtil.createViewPropertyId(view.getId(), mProperty.getName()));
				lp.setName(bigMap.getString("viewpropertyname", mProperty.getDescription()));
				lp.setPropertyId(mProperty.getId());
			}
		} else {
			if (mProperty == null) {
				lp.setId(MUtil.createViewPropertyId(view.getId(), mRelation.getId()));
				lp.setName(bigMap.getString("viewpropertyname", mRelation.getName()));
				lp.setRelationId(mRelation.getId());
			} else {
				lp.setId(MUtil.createViewPropertyId(view.getId(), mProperty.getId()));
				lp.setName(bigMap.getString("viewpropertyname", mRelation.getRelatedObject().getName() + mProperty.getDescription()));
				lp.setPropertyId(mProperty.getId());
				lp.setRelationId(mRelation.getId());
			}
		}
		lp.setIndex(targetVp.getIndex() + 5);
		if (view.isList()) {
			lp.setType(bigMap.getString("listinputtype"));
			lp.setWidth(bigMap.getInt("listwidth", 100));
			lp.setAjax(bigMap.getString("listajax"));
			lp.setAjaxSql(bigMap.getString("listsql"));
			setOrder(bigMap.getInt("order", -1), bigMap.getString("ordertype"), lp);
		} else if (view.isForm()) {
			lp.setType(bigMap.getString("forminputtype"));
			if (lp.getType() == null) {
				if (mRelation.isM21() || mRelation.isM2M()) {
					lp.setType(MViewProperty.TYPE_OBJECT_SELECTOR);
				}
				if (mRelation.isM2C1() || mRelation.isM2CM()) {
					lp.setType(MViewProperty.TYPE_OBJECT_INPUTOR);
				}
			}
			lp.setInitValue(bigMap.getString("forminitvalue"));
			lp.setReplaceInit(bigMap.getInt("forminitreplaceempty", 0) + bigMap.getInt("forminitreplacevalue", 0) * 2);
			lp.setSubmitValue(bigMap.getString("formsubmitvalue"));
			lp.setReplaceSubmit(bigMap.getInt("formsubmitreplaceempty", 0) + bigMap.getInt("formsubmitreplacevalue", 0) * 2);
			lp.setAllowsBlank(bigMap.getInt("required", 0) == 1 ? 0 : 1);
			lp.setReadOnly(bigMap.getInt("readonly", 0));
			lp.setSubmit(bigMap.getInt("submit", 0));
			lp.setAjax(bigMap.getString("formajax"));
		} else if (view.isSearch()) {
			lp.setType(bigMap.getString("searchinputtype"));
			lp.setCompareType(bigMap.getString("comparetype"));
			lp.setCompareExpression(bigMap.getString("searchexpression"));
			lp.setCompareExpression(bigMap.getString("searchdictionarytype"));
			lp.setListFilter(bigMap.getString("listfilter"));
		}
		view.getProperties().add(lp);
		Collections.sort(view.getProperties());
		saveViewByProperty(lp);
	}

	@Resource
	private MDictionaryMapper dictionaryMapper;

	@Override
	public void createViewPropertyByDictionary(String app, String viewproperty, Map<String, Object> req) {
		BigMap<String, Object> bigMap = new BigMap<String, Object>().bigPutAll(req);
		// 1.创建数据字典
		String name = bigMap.getString("name");
		if (WoKit.isEmpty(name)) {
			throw new MException(MException.ERROR_NULL, "名称");
		}
		String type = bigMap.getString("type");
		String dictionaryvalue = bigMap.getString("dictionaryvalue");
		String dictionaryname = bigMap.getString("dictionaryname");
		String dictionarytype = bigMap.getString("dictionarytype");
		switch (type) {
		case "1":
			if (WoKit.isEmpty(dictionaryname)) {
				throw new MException(MException.ERROR_NULL, "字典名称");
			}
			if (WoKit.isEmpty(dictionarytype)) {
				dictionarytype = MDictionaryDto.PREFIX_TYPE + WoPinyinKit.getPinyinLower(name);
			}
			String[] dictionarynameStrings = dictionaryname.split(",");
			String[] dictionarynameValues = new String[dictionarynameStrings.length];
			for (int i = 0; i < dictionarynameValues.length; i++) {
				dictionarynameValues[i] = WoKit.generateFixedLenNo(2, i + 1);
			}
			createDictionaries(name, dictionarytype, dictionarynameStrings, dictionarynameValues);
			break;
		case "2":
			if (WoKit.isEmpty(dictionaryvalue)) {
				throw new MException(MException.ERROR_NULL, "字典值");
			}
			if (WoKit.isEmpty(dictionaryname)) {
				throw new MException(MException.ERROR_NULL, "字典名称");
			}
			if (WoKit.isEmpty(dictionarytype)) {
				dictionarytype = MDictionaryDto.PREFIX_TYPE + WoPinyinKit.getPinyinLower(name);
			}
			dictionarynameStrings = dictionaryname.split(",");
			dictionarynameValues = dictionaryvalue.split(",");
			createDictionaries(name, dictionarytype, dictionarynameStrings, dictionarynameValues);
			break;
		default:
			if (WoKit.isEmpty(dictionarytype)) {
				throw new MException(MException.ERROR_NULL, "字典类型");
			}
			Integer count = dictionaryMapper.selectCount(new QueryWrapper<MDictionary>().eq("type", dictionarytype));
			if (count == 0) {
				throw new MException(MException.DIC_TYPE, dictionarytype);
			}
			break;
		}
		bigMap.bigPut("dictionarytype", dictionarytype);
		// 2.创建或者选择属性
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		MProperty mProperty = null;
		if (bigMap.bigEquals("propertyselect", "1")) {// 选择属性
			mProperty = targetVp.getView().getApp().getObject().getProperty(bigMap.getString("property"));
			if (mProperty == null) {
				throw new MException(MException.BIZ_PROP);
			}
		} else {// 创建属性
			String detail = "类型:文本,长度:5";
			if (targetVp.getProperty() != null) {
				detail += ",索引:" + (targetVp.getProperty().getIndex() + 1);
			}
			mProperty = metaService.createPropertyMeta(targetVp.getView().getObjectId(), name, detail);
		}
		// 3.创建视图属性
		createViewProperties(targetVp, bigMap, mProperty, null);
	}

	private void createDictionaries(String name, String dictionarytype, String[] dictionarynameStrings,
			String[] dictionarynameValues) {
		if (dictionarynameStrings.length != dictionarynameValues.length) {
			throw new MException(MException.DIC_NAMEVALUE);
		}
		// 删除同样类型的数据字典
		dictionaryMapper.delete(new QueryWrapper<MDictionary>().eq("type", dictionarytype));
		// 创建数据字典
		for (int i = 0; i < dictionarynameValues.length; i++) {
			MDictionary dictionary = new MDictionary();
			dictionary.setType(dictionarytype);
			dictionary.setName(dictionarynameStrings[i]);
			dictionary.setValue(dictionarynameValues[i]);
			dictionary.getData().bigPut(MDictionaryDto.PARAM_TYPE_NAME, name);
			dictionaryMapper.insert(dictionary);
		}
	}

	@Override
	public void createViewPropertyByRelation(String app, String viewproperty, Map<String, Object> req) {
		MViewProperty targetVp = getViewProperty(app, viewproperty);
		BigMap<String, Object> bigMap = new BigMap<String, Object>().bigPutAll(req);
		if (bigMap.isEmpty("type")) {
			throw new MException(MException.ERROR_NULL, "类型");
		}
		String relatedObjectIds = bigMap.getString("relatedobjectid").replace(',', '|').replace("m_object:", "");
		String relationDetail = "类型:" + bigMap.getString("type") + ",关联:" + relatedObjectIds;
		if (!bigMap.isEmpty("default")) {
			relationDetail += ",默认值:" + bigMap.getString("default");
		}
		List relationMeta = metaService.createRelationMeta(targetVp.getView().getObjectId(), bigMap.getString("name"), relationDetail);
		relationMeta.forEach(e -> {
			if (e instanceof MRelation) {
				createViewProperties(targetVp, bigMap, null, (MRelation) e); 
			}
		});
	}

	private MButton getButton(String app, String button) {
		MApp mApp = appMapper.selectById(app);
		MAppDto appDto = new MAppDto(mApp);
		MButton targetButton = null;
		for (Iterator<MViewDto> iterator = appDto.getViews().iterator(); iterator.hasNext();) {
			MViewDto view = iterator.next();
			targetButton = view.getButton(button);
			if (targetButton != null) {
				break;
			}
		}
		return targetButton;
	}
	
	private void saveViewByButton(MButton targetVp) {
		// 保存到数据库
		MView mView = viewMapper.selectById(targetVp.getView().getId());
		mView.setButtons(targetVp.getView().getButtons());
		viewMapper.updateById(mView);
	}
	
	@Override
	public void createButton(String app, String v, String button, MButton mButton) {
		if (WoKit.isEmpty(mButton.getType())) {
			throw new MException(MException.ERROR_NULL, "类型");
		}
		if ("custom".equals(mButton.getType())) {
			mButton.setType(null);
			if (mButton.getData().isEmpty(MObjectDto.PARAM_URL)) {
				throw new MException(MException.ERROR_NULL, "URL");
			}
		}
		if (MButton.TYPE_OPEN_TAB.equals(mButton.getType())) {
			if (mButton.getData().isEmpty(MButton.PARAM_TAB_VIEW) && mButton.getData().isEmpty(MButton.PARAM_TAB_APP) && mButton.getData().isEmpty(MButton.PARAM_TAB_URL)) {
				throw new MException(MException.ERROR_NULL, "openTab类型按钮的配置");
			}
			if (!mButton.getData().isEmpty(MButton.PARAM_TAB_VIEW) && mButton.getData().isEmpty(MButton.PARAM_TAB_APP)) {
				throw new MException(MException.ERROR_NULL, "应用ID");
			}
		}
		if (MButton.TYPE_REQUEST_JSON.equals(mButton.getType())) {
			if (mButton.getData().isEmpty(MButton.PARAM_URL_REQ)) {
				throw new MException(MException.ERROR_NULL, "json请求的URL");
			}
		}
		String idSuffixString = (WoKit.isEmpty(mButton.getName()) ? String.valueOf(System.currentTimeMillis() % 10000) : WoPinyinKit.getPinyinLower(mButton.getName()));
		if (WoKit.isEmpty(button)) {
			MView mView = viewMapper.selectById(v);
			mButton.setId(mView.getId() + "_" + idSuffixString);
			setTabType(mButton);
			mView.getButtons().add(mButton);
			viewMapper.updateById(mView);
		} else {
			MButton b = getButton(app, button);
			MViewDto view = b.getView();
			List<MButton> buttons = view.getButtons();
			view.reIndexButtons();
			mButton.setId(view.getId() + "_" + idSuffixString);
			mButton.setIndex(b.getIndex() + 5);
			setTabType(mButton);
			buttons.add(mButton);
			Collections.sort(buttons);
			saveViewByButton(b);
		}
	}

	private void setTabType(MButton mButton) {
		if (MButton.TYPE_OPEN_TAB.equals(mButton.getType())) {
			String viewIdString = mButton.getData().getString(MButton.PARAM_TAB_VIEW);
			MApp tabApp = appMapper.selectById(mButton.getData().getString(MButton.PARAM_TAB_APP));
			if (!WoKit.isEmpty(viewIdString)) {
				if (tabApp == null) {
					throw new MException(MException.APP_NO, mButton.getData().getString(MButton.PARAM_TAB_APP));
				}
				mButton.setTabAppData(tabApp);
				if (viewIdString.equals(tabApp.getData().getString(MCrud.PARAM_CREATE))) {
					mButton.setTabType(MCrud.PARAM_CREATE);
				} else if (viewIdString.equals(tabApp.getData().getString(MCrud.PARAM_UPDATE))) {
					mButton.setTabType(MCrud.PARAM_UPDATE);
				} else if (new MAppDto(tabApp).getView(viewIdString) != null) {
					mButton.setTabType(MCrud.PARAM_UPDATE);
				} else {
					throw new MException(MException.CRUD_NOVW, viewIdString);
				}
			} else if (!mButton.getData().isEmpty(MButton.PARAM_TAB_APP)) {
				if (tabApp == null) {
					throw new MException(MException.APP_NO, mButton.getData().getString(MButton.PARAM_TAB_APP));
				}
				mButton.setTabAppData(tabApp);
				mButton.setTabType("app");
			} else {
				mButton.setTabType("url");
			}
		}
	}

	@Override
	public void moveButton(String app, String button, Integer offset) {
		MButton mButton = getButton(app, button);
		MViewDto view = mButton.getView();
		List<MButton> buttons = view.getButtons();
		Collections.sort(buttons);
		Integer index = -1;
		for (int i = 0; i < buttons.size(); i++) {
			buttons.get(i).setIndex((i + 1) * 10);
			if (mButton == buttons.get(i)) {
				index = i;
			}
		}
		mButton.setIndex((index + 1)*10 + 15 * offset);
		Collections.sort(buttons);
		saveViewByButton(mButton);
	}

	@Override
	public void deleteButton(String app, String button) {
		MButton mButton = getButton(app, button);
		mButton.getView().getButtons().remove(mButton);
		saveViewByButton(mButton);
	}

	@Override
	public void setListRows(String list, Integer rows) {
		MView view = viewMapper.selectById(list);
		view.getData().bigPut(MViewDto.PARAM_PAGE_SIZE, rows).bigPut(MViewDto.PARAM_PAGING, "true");
		viewMapper.updateById(view);
	}

	@Override
	public void unpage(String list) {
		MView view = viewMapper.selectById(list);
		view.getData().bigPut(MViewDto.PARAM_PAGING, "false");
		viewMapper.updateById(view);
	}
	
}
